package cn.zf233.xcloud.util;

import cn.zf233.xcloud.commom.Const;
import cn.zf233.xcloud.commom.enums.*;
import cn.zf233.xcloud.entity.File;
import cn.zf233.xcloud.entity.User;
import cn.zf233.xcloud.vo.AdminVo;
import cn.zf233.xcloud.vo.FileVo;
import cn.zf233.xcloud.vo.UserVo;
import org.apache.commons.lang.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * Created by zf233 on 2021/8/2
 * 通用工具类
 */
public class CommonUtil {

    private static final Base64.Decoder DECODER = Base64.getDecoder();
    private static final Base64.Encoder ENCODER = Base64.getEncoder();

    // 获取用户展示昵称，优先昵称显示
    public static String getDisplayName(User user) {
        if (user == null) {
            throw new RuntimeException();
        }
        if (StringUtils.isNotBlank(user.getNickname())) {
            return user.getNickname();
        }
        String nickname;
        switch (Objects.requireNonNull(UserSourceENUM.exists(user.getSource()))) {
            case QQ_USER:
                if (StringUtils.isNotBlank(user.getEmail())) {
                    nickname = user.getEmail();
                } else if (StringUtils.isNotBlank(user.getPhone())) {
                    nickname = user.getPhone();
                } else {
                    nickname = "QQ用户";
                }
                break;
            case EMAIL_USER:
                nickname = user.getEmail();
                break;
            case PHONE_USER:
                nickname = user.getPhone();
                break;
            default:
                nickname = "未设置";
        }
        return nickname;
    }

    // 时间单位转换
    public static String timeoutSwitch(long timeout) {
        if (timeout > 0) {
            long day = timeout / TimeUnit.DAYS.toMillis(1L);
            long hour = (timeout - TimeUnit.DAYS.toMillis(day)) / TimeUnit.HOURS.toMillis(1L);
            long minute = (timeout - TimeUnit.DAYS.toMillis(day) - TimeUnit.HOURS.toMillis(hour)) / TimeUnit.MINUTES.toMillis(1L);
            long second = (timeout - TimeUnit.DAYS.toMillis(day) - TimeUnit.HOURS.toMillis(hour) - TimeUnit.MINUTES.toMillis(minute)) / 1000L;
            return String.format("%2d", day) + "天" + String.format("%2d", hour) + "小时" + String.format("%2d", minute) + "分钟" + String.format("%2d", second) + "秒";
        }
        return "已超时";
    }

    // 6位数字验证码
    public static String vCode() {
        StringBuilder builder = new StringBuilder();
        Random random = new Random();
        for (int i = 0; i < 6; i++) {
            builder.append(random.nextInt(10));
        }
        return builder.toString();
    }

    // 组装用户请求对象 VO -> DAO
    public static User assembleUser(UserVo userVo) {
        return assembleUser(userVo.getId());
    }

    // 组装用户请求对象 VO -> DAO
    public static User assembleUser(Long id) {
        User user = new User();
        user.setId(id);
        return user;
    }

    // 组装用户管理展示对象
    public static AdminVo assembleAdminVoDetail(User user) {
        AdminVo adminVo = new AdminVo();
        adminVo.setId(user.getId());
        adminVo.setHeadUrl(user.getHeadUrl());
        adminVo.setDisplayName(CommonUtil.getDisplayName(user));
        adminVo.setRole(UserSourceENUM.getUserTypeName(user.getSource()));
        adminVo.setStatus(UserStatusENUM.getUserStatusName(user.getStatus()));
        adminVo.setSource(UserSourceENUM.getUserTypeName(user.getSource()));
        adminVo.setStatusNum(user.getStatus());
        adminVo.setLevel(user.getLevel());
        adminVo.setCreateTime(ThreadSafeDateUtil.format(user.getCreateTime()));
        return adminVo;
    }

    // 组装用户展示对象
    public static UserVo assembleUserVoDetail(User user) {
        UserVo userVo = new UserVo();
        userVo.setId(user.getId());
        userVo.setUid(user.getUid());
        userVo.setAcceptAgree(user.getAcceptAgree());
        userVo.setHeadUrl(user.getHeadUrl());
        userVo.setEmail(user.getEmail());
        userVo.setDisplayName(user.getNickname());
        userVo.setNickname(user.getNickname());
        userVo.setPhone(user.getPhone());
        userVo.setSource(UserSourceENUM.getUserTypeName(user.getSource()));
        userVo.setSourceNum(user.getSource());
        userVo.setRole(UserRoleENUM.getUserRoleName(user.getRole()));
        userVo.setRoleNum(user.getRole());
        userVo.setAvailableDownloadCapacity(DataUnitENUM.BYTE.decimalString(user.getDownloadFlow()));
        userVo.setFreeShareCount(user.getShareNumber());
        userVo.setUseCapacity(DataUnitENUM.BYTE.decimalString(user.getUseCapacity()));
        userVo.setCapacity(DataUnitENUM.BYTE.decimalString(user.getCapacity()));
        userVo.setUseCapacityNum(user.getUseCapacity());
        userVo.setCapacityNum(user.getCapacity());
        userVo.setLevel(user.getLevel());
        userVo.setStatus(user.getStatus());
        userVo.setGrowthValue(user.getGrowthValue());
        userVo.setCreateTime(ThreadSafeDateUtil.format(user.getCreateTime()));
        return userVo;
    }

    // 组装文件展示对象（批量）
    public static List<FileVo> assembleFileVoDetail(List<File> fileList) {
        return fileList.stream().map(CommonUtil::assembleFileVoDetail).collect(Collectors.toList());
    }

    // 组装文件展示对象
    public static FileVo assembleFileVoDetail(File file) {
        FileVo fileVo = new FileVo();
        fileVo.setFileSize(file.isAFolder() ? "-" : DataUnitENUM.BYTE.decimalString(file.getFileSize()));
        fileVo.setFileName(file.getOldFileName());
        fileVo.setHighLightFileName(file.getHighLightFileName());
        fileVo.setLogoUrl(file.isAFolder() ? ClassifyENUM.FOLDER.getLogo() : getLogoUrl(file));
        fileVo.setUploadTime(ThreadSafeDateUtil.format(file.getUploadTime()));
        Integer folder = file.getFolder();
        fileVo.setFolder(folder);
        fileVo.setId(file.getId());
        fileVo.setParentId(file.getParentId());
        return fileVo;
    }

    // 根据文件名获取Logo地址
    private static String getLogoUrl(File file) {
        String uri = Const.XCLOUD_FILE_TYPE_ICON_MAP.get(getExtName(file.getOldFileName()));
        if (StringUtils.isBlank(uri)) {
            return ClassifyENUM.classify(file.getClassify()).getLogo();
        } else {
            return Const.XCLOUD_FILE_TYPE_ICON_PREFIX + uri;
        }
    }

    // 根据文件名获取扩展名
    private static String getExtName(final String name) {
        return name.substring(name.lastIndexOf(".") + 1).toLowerCase();
    }

    public static String writeHeadPhoto(InputStream in, Long userId, String fileName) {
        FileOutputStream out = null;
        try {
            String name = Const.USER_HEAD_FILE_NAME_PREFIX + userId + fileName.substring(fileName.lastIndexOf("."));
            java.io.File file = new java.io.File(Const.USER_HEAD_FILE_REAL_PATH, name);
            if (file.exists()) {
                boolean flag = file.delete();
                if (!flag) {
                    return null;
                }
            }
            boolean flag = file.createNewFile();
            if (!flag) {
                return null;
            }
            out = new FileOutputStream(file);
            byte[] bytes = new byte[Math.toIntExact(DataUnitENUM.MB.bytes())];
            int readCount;
            while ((readCount = in.read(bytes)) != -1) {
                out.write(bytes, 0, readCount);
            }
            out.flush();
            return name;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    // 获取请求的真实IP地址
    public static String getRealIp(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        String unknown = "unknown";
        if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }

    public static String base64Encoder(String str) {
        return ENCODER.encodeToString(str.getBytes(StandardCharsets.UTF_8));
    }

    public static String base64Decoder(String str) {
        try {
            return new String(DECODER.decode(str));
        } catch (Exception e) {
            return null;
        }
    }

    // 雪花算法工具类
    public static class SnowFlakeUtil {

        private final long MACHINE_BIT = 5;   //机器标识占用的位数
        private final long DATACENTER_BIT = 5;//数据中心占用的位数

        private final long datacenterId;
        private final long machineId;
        private long sequence = 0L;
        private long lastStmp = -1L;

        private static final SnowFlakeUtil snowFlakeUtil = new SnowFlakeUtil(0, 0);

        private SnowFlakeUtil(long datacenterId, long machineId) {
            long MAX_DATACENTER_NUM = ~(-1L << DATACENTER_BIT);
            if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {
                throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");
            }
            long MAX_MACHINE_NUM = ~(-1L << MACHINE_BIT);
            if (machineId > MAX_MACHINE_NUM || machineId < 0) {
                throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");
            }
            this.datacenterId = datacenterId;
            this.machineId = machineId;
        }

        private synchronized long nextId() {
            long currStmp = getNewStmp();
            if (currStmp < lastStmp) {
                throw new RuntimeException("Clock moved backwards.  Refusing to generate id");
            }
            //序列号占用的位数
            long SEQUENCE_BIT = 12;
            if (currStmp == lastStmp) {
                long MAX_SEQUENCE = ~(-1L << SEQUENCE_BIT);
                sequence = (sequence + 1) & MAX_SEQUENCE;
                if (sequence == 0L) {
                    currStmp = getNextMill();
                }
            } else {
                sequence = 0L;
            }
            lastStmp = currStmp;
            long START_STMP = 1591753401773L;
            long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
            long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;
            return (currStmp - START_STMP) << TIMESTMP_LEFT //时间戳部分
                    | datacenterId << DATACENTER_LEFT       //数据中心部分
                    | machineId << SEQUENCE_BIT             //机器标识部分
                    | sequence;                             //序列号部分
        }

        private long getNextMill() {
            long mill = getNewStmp();
            while (mill <= lastStmp) {
                mill = getNewStmp();
            }
            return mill;
        }

        private long getNewStmp() {
            return System.currentTimeMillis();
        }

        public static Long generateId() {
            return snowFlakeUtil.nextId();
        }
    }

}
